// LOCK DB VED ALLE DB READS

// TODO: FilePlayer og DVDPlayer page
// TODO: Keyboard / Remote control
// TODO: Fix Harddisk Page
// TODO: MessageBox


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.IO;
using SharpMedia.Pages;
using Lizk.SimpleHUD;

using System.Diagnostics;
using System.Threading;
using Lizk.WebScraper;
using System.Reflection;
using Lizk.DShowPlayer;
using System.Text;
using System.Web;
using SharpMedia.Controls;
using Lizk.Utilities;
using MediaData;
using System.Linq;

namespace SharpMedia
{
    public static class SharpMedia
    {
        /// <summary>
        /// Contains a reference to the main thread.
        /// </summary>
        private static Thread updateThread;
        private static HUDEngine hud;
        private static HUDPageHandler pages;
        private static OverlayPage2 overlay;
        private static bool Initialized = false;
        public static HUDRenderer renderer;
        public static HUDLabel DebugLabel { get; private set; }

        public static string RootDirectory { get; set; }
        public static string DataDirectory { get; set; }
        public static Engine Engine { get; set; }
        public static Settings Settings;
        public static HUDPage CurrentPage { get { return pages.CurrentPage; } }

        public static ImageCache ImageCache { get; private set; }
        public static ScraperFileCache ScraperCache { get; private set; }
        public static ScraperHandler ScraperHandler { get; private set; }
        public static ExecutionQueue JobQueue { get; set; }

        public static SQLiteTypedDBTestThing<MediaDataSet> Database { get; private set; }
        public static MediaDataImporter Importer;
        public static MediaRootManager RootManager;

        private static InterfaceLock iLock = new InterfaceLock();

        public static bool LoadSettings(string dataDirectory)
        {
            DataDirectory = dataDirectory;
            // Create the data directory
            if (!Directory.Exists(DataDirectory))
                Directory.CreateDirectory(DataDirectory);

           
            return Settings.TryLoadSettings(Path.Combine(DataDirectory, "settings.xml"), out Settings);
        }

        private static object initObject = new object();
        public static void Init(Thread mainThread)
        {
            lock (initObject)
            {
                if (Initialized)
                    return;
                ErrorHandling.Initialize(Path.Combine(RootDirectory, "errors.log"));
                iLock.Unlocked += new EventHandler(iLock_Unlocked);

                JobQueue = new ExecutionQueue();


                hud = new HUDEngine(renderer);

                DebugLabel = new HUDLabel("", "tahoma14b")
                {
                    Size = new SizeS(hud.Size.Width, 20),
                    Anchor = Anchor.All ^ Anchor.Bottom,
                    Alpha = Debugger.IsAttached ? 255 : 0
                };

                pages = new HUDPageHandler()
                {
                    Size = hud.Size,
                    Anchor = Anchor.All
                };
                HUDImage img = new HUDImage(@"blank-background.jpg")
                {
                    ConstrainProportions = false,
                    Anchor = Anchor.All,
                    FadeOnLoad = true,
                    Size = hud.Size,
                    Alpha = 0
                };
                hud.Add(img);

                HUDLabel loadLabel = new HUDLabel("Loading...", "tahoma30b")
                {
                    Size = new SizeS(hud.Size.Width, 50),
                    Anchor = Anchor.All ^ Anchor.Top,
                    Alignment = Lizk.SimpleHUD.Alignment.Bottom,
                    Alpha = Debugger.IsAttached ? 255 : 0,
                    Location = new PointS(10, hud.Size.Height - 60)
                };
                hud.Add(loadLabel);
                hud.Add(pages);
                hud.Size = renderer.GetCanvasSize(); // breaks if moved before things are added. 

                Initialized = true;

                // Initialize Log File

     
                Database = new SQLiteTypedDBTestThing<MediaDataSet>(Path.Combine(DataDirectory, "media.db"));
                Database.Connect();
                Database.LoadData();

                DVDPlayer.BookMarkFile = Path.Combine(DataDirectory, "DVDBookmarks.bmk");

                if (Directory.Exists(Path.Combine(RootDirectory, "plugins")))
                    LoadScraperPlugins(Path.Combine(RootDirectory, "plugins"));
                updateThread = mainThread;

                ImageCache = new ImageCache(Path.Combine(DataDirectory, "cache"));
                ScraperCache = new ScraperFileCache(Path.Combine(DataDirectory, "cache"));
                ScraperHandler = new ScraperHandler(Settings.MediaCategories.ToArray());
                ScraperHandler.ScrapeComplete += new EventHandler<ScraperHandler.ScraperStatusArgs>(ScraperHandler_ScrapeComplete);

                RootManager = new MediaRootManager((from s in SharpMedia.Settings.MediaCategories select s.Name).ToArray());
                RootManager.Refresh();

                
                Importer = new MediaDataImporter(RootManager, Database.Data, new FileCache[] { ImageCache, ScraperCache });

                foreach (var category in Settings.MediaCategories)
                {
                    Type t = Type.GetType(category.ImporterClass);
                    Importer.Scanners.Add((MediaItemScanner)t.GetConstructor(new Type[] { typeof(string) }).Invoke(new object[] { category.Name }));
                }

                /*Importer.Scanners.Add(new TVScanner("tvshows") { SortSeasons = false });
                Importer.Scanners.Add(new AnimeScanner("anime"));
                Importer.Scanners.Add(new MovieScanner("movies"));
                */

                ScraperCache.LoadCompleteCache(Path.Combine(DataDirectory, "scrapercache.bin"));


                overlay = new OverlayPage2()
                {
                    Alpha = 255,
                    Size = hud.Size
                };

                var firstPage = new MainMenuPage(overlay) { Alpha = 255 };
                JobQueue.Enqueue(delegate
                {
                    Navigate(firstPage);
                    hud.Add(overlay);
                    hud.Add(DebugLabel);
                });
                loadLabel.Fade(0, 2f);
                img.Fade(255, 3f);

            }

            //Application.Run(Form);
           

        }

        public static HUDControl.MouseButtons PressedMouseButtons { get; private set; }

        public static void MouseUp(PointS location, HUDControl.MouseButtons buttons)
        {
            if (!Initialized)
                return;
            PressedMouseButtons ^= buttons;
            pages.MouseUp(location, buttons);
        }

        public static void MouseDown(PointS location, HUDControl.MouseButtons buttons)
        {
            if (!Initialized)
                return;
            PressedMouseButtons |= buttons;
            pages.MouseDown(location, buttons);
        }


        static void iLock_Unlocked(object sender, EventArgs e)
        {
            Database.Data.AcceptChanges();
            Util.RunAsync(CommitDatabase, Database.Data.LastChanges);
        }

        static void ScraperHandler_ScrapeComplete(object sender, ScraperHandler.ScraperStatusArgs e)
        {
            if (!ImageCache.Contains(e.Result.Title) && !string.IsNullOrEmpty(e.Result.Result.ImageUrl))// && !SharpMedia.ArtworkCache.Contains(title["type"].ToString(), title["name"].ToString()))
            {
                string image;
                if (e.Result.Scraper.TryDownloadImage(e.Result.Result.ImageUrl, out image))
                {
                    ImageCache.Save(e.Result.Title, image);
                }
            }

            iLock.WaitForLock();

            lock (Database.Data.LockObject)
            {
                ScraperCache.Save(e.Result.Title, e.Result.Result);
                Importer.DistributeCaches(e.Result.Title);                
            }

            iLock.WorkDone();

        }

        
        public static void Render()
        {
            if (!Initialized)
                return;

            renderer.BeginDraw();
            hud.Size = renderer.GetCanvasSize();
            hud.PaintContainer(255, hud.Bounds, hud.Bounds, hud);
            renderer.EndDraw();
        }


        public static void Closing()
        {
            lock (Database.Data.LockObject)
            {
                Database.Data.AcceptChanges();
                Database.Commit(Database.Data.LastChanges);
            }

            ScraperCache.SaveCompleteCache(Path.Combine(DataDirectory, "scrapercache.bin"));

            Settings.WindowedMode = Engine.Windowed;
            if (Settings.WindowedMode == WindowMode.Windowed)
                Settings.WindowSize = Engine.WindowSize;

            XmlSettings.saveXml(Settings, Path.Combine(DataDirectory, "settings.xml"));
        }

        public static void Navigate(HUDPage page) { SharpMedia.Engine.Wait(); pages.Navigate(page); SharpMedia.Engine.Resume();  }
        public static void Back() { SharpMedia.Engine.Wait(); pages.Back(); SharpMedia.Engine.Resume(); }
        


        public static bool UpdateLock
        {
            get
            {
                return iLock.IsLocked && isUpdateLocked;
            }
        }

        public static object UpdateLockObject
        {
            get { return iLock.lockObject; }
        }

        public static void WaitForLock()
        {
            if (Thread.CurrentThread == updateThread)
            {
                iLock.Lock();
            }
            iLock.WaitForLock();
        }

        public static void WorkDone() { iLock.WorkDone(); }


        private static bool isUpdateLocked = false;

        public static event Action<MediaDataSet> MediaChanged;


        public static void Update()
        {
            if (!Initialized)
                return;



            TimeSpan timeElapsed = SharpMedia.Engine.TimeElapsed;
            lock (iLock.lockObject)
            {
                if (iLock.WaitingForLock && !iLock.IsLocked)
                {
                    iLock.Lock();

                }

                if (iLock.IsLocked && !isUpdateLocked)
                {
                    isUpdateLocked = true;
                    overlay.InterfaceLocked();
                }

                if (isUpdateLocked && !iLock.IsLocked)
                {
                    isUpdateLocked = false;
                    overlay.InterfaceUnlocked();

                    if (MediaChanged != null)
                        MediaChanged(Database.Data.LastChanges);

                }
            }

            //DebugLabel.Text = Form.DebugMessage;
            PointS p = !Engine.IsCursorHidden ? new PointS((int)Engine.MousePosition.X, (int)Engine.MousePosition.Y) : new PointS(-1,-1);
            hud.UpdateMousePosition(p);
            hud.Update(timeElapsed);




            JobQueue.ExecuteQueue();
        }


        private static void CommitDatabase(MediaDataSet changes)
        {
            lock (Database.Data.LockObject)
            {
                Database.Commit(changes);
            }
        }

        public static void HandleInput(object sender, CommandEventArgs command)
        {
            if (!Initialized)
                return;

            if (pages.CurrentPage != null)
            {
                JobQueue.Enqueue(pages.CurrentPage.Command, command.Command, command.Tag);
            }
        }

        public static bool CanUseDatabase
        {
            get
            {
                lock (SharpMedia.UpdateLockObject)
                {
                    if (!SharpMedia.UpdateLock)
                    {
                        return true;
                    }
                    else
                    {
                        overlay.ShowLockedWarning();
                        return false;
                    }
                }
            }
        }

        public static void NotifyMediaChanges()
        {
            if (MediaChanged != null)
                MediaChanged(null);
        }

        private static void LoadScraperPlugins(string pluginDir)
        {
            foreach (string ass in Directory.GetFiles(pluginDir, "*.dll"))
            {
                Assembly a = Assembly.LoadFile(Path.GetFullPath(ass));
                Scraper.LoadPluginAssembly(a);
            }
        }


        #region TV

        /*private static void LoadTuners(string file)
        {
            Tuners = new List<TvSettings>();
            if (File.Exists(file))
            {
                try
                {
                    Tuners = XmlSettings.loadXml<List<TvSettings>>(file);
                }
                catch { Tuners = new List<TvSettings>(); }
            }
        }
         
                 public static TvSettings TVAvailable()
        {
            foreach (InputDevice device in TVPlayer.CaptureDevices)
            {
                foreach (TvSettings tuner in SharpMedia.Tuners)
                {
                    if (tuner.Device.DevicePath == device.DevicePath)
                        return tuner;
                }
            }
            return null;
        }

         
         */

        #endregion

    }

    


}